﻿// ------------------------------------------------------------------------
// Copyright 2024 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

using System.Diagnostics.CodeAnalysis;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1.Dapr;

namespace Dapr.AI.Conversation;

/// <summary>
/// <para>
/// Used to interact with the Dapr conversation building block.
/// Use <see cref="DaprConversationClientBuilder"/> to create a <see cref="DaprConversationClient"/> or register
/// for use with dependency injection via
/// <see><cref>DaprJobsServiceCollectionExtensions.AddDaprJobsClient</cref></see>.
/// </para>
/// <para>
/// Implementations of <see cref="DaprConversationClient"/> implement <see cref="IDisposable"/> because the
/// client accesses network resources. For best performance, create a single long-lived client instance
/// and share it for the lifetime of the application. This is done for you if created via the DI extensions. Avoid
/// creating a disposing a client instance for each operation that the application performs - this can lead to socket
/// exhaustion and other problems.
/// </para>
/// </summary>
[Experimental("DAPR_CONVERSATION", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/")]
public abstract class DaprConversationClient : DaprAIClient
{
    /// <summary>
    /// The HTTP client used by the client for calling the Dapr runtime.
    /// </summary>
    /// <remarks>
    /// Property exposed for testing purposes.
    /// </remarks>
    internal readonly HttpClient HttpClient;
    /// <summary>
    /// The Dapr API token value.
    /// </summary>
    /// <remarks>
    /// Property exposed for testing purposes.
    /// </remarks>
    internal readonly string? DaprApiToken;
    /// <summary>
    /// The autogenerated Dapr client.
    /// </summary>
    /// <remarks>
    /// Property exposed for testing purposes.
    /// </remarks>
    internal Autogenerated.DaprClient Client { get; }
    
    /// <summary>
    /// Used to initialize a new instance of a <see cref="DaprConversationClient"/>.
    /// </summary>
    /// <param name="client">The Dapr client.</param>
    /// <param name="httpClient">The HTTP client used by the client for calling the Dapr runtime.</param>
    /// <param name="daprApiToken">An optional token required to send requests to the Dapr sidecar.</param>
    protected DaprConversationClient(Autogenerated.DaprClient client,
        HttpClient httpClient,
        string? daprApiToken = null)
    {
        this.Client = client;
        this.HttpClient = httpClient;
        this.DaprApiToken = daprApiToken;
    }

    /// <summary>
    /// Sends various inputs to the large language model via the Conversational building block on the Dapr sidecar.
    /// </summary>
    /// <param name="inputs">The inputs for the conversation.</param>
    /// <param name="options">Options used to configure the conversation.</param>
    /// <param name="cancellationToken">Cancellation token.</param>
    /// <returns>The response(s) provided by the LLM provider.</returns>
    public abstract Task<ConversationResponse> ConverseAsync(IReadOnlyList<ConversationInput> inputs,
        ConversationOptions options, CancellationToken cancellationToken = default);
}
